#if !defined(RT_HR_H)
#define RT_HR_H
// hr.h
//
// Description: Handling unexpected COM HRESULTs as C++
// exceptions.
// 
// The utilities provided here aid in debugging (and
// logging after delivery) as they record the source
// file/line location where the error was encountered.
// 
// Setting breakpoints on the throw statemens in this
// header file will stop execution for all errors
// encountered by the program through THR(), TWS(), etc.,
// before the exception is thrown, allowing you to check
// the call stack for the source of the error in the
// debugger.
//
// This file is meant to be used to trap unexepected
// errors.  For expected error conditions (such as the
// HRESULTs returned by IDirect3D8::CheckDeviceType, or
// the D3DERR_DEVICELOST result returned by
// IDirect3DDevice8::Present), you should explicitly test
// against expected failure codes and only THR() on error
// code you do not expect.
//
// This will avoid the cost of exceptions for the normal
// flow of control, and the overhead of exceptions is
// perfectly reasonable when you expect the call to succeed
// but it fails anyway.  These failures usually represent
// an invalid parameter passed to Direct3D and the returned
// HRESULT will be D3DERR_INVALIDCALL with coresponding
// additional information in the debug output.
//
// Provides:
//  hr_message
//      Exception class that records a failed HRESULT,
//      file/line source file pair indicating where the
//      failed HRESULT was generated, and possible context
//      message.  Its constructor (not inlined) looks up
//      the HRESULT via FormatMessage() and a few other
//      places specific to DirectX to generate the message
//      text.
//
//  display_error
//      Displays a message box containing the error string
//      inside an hr_message and returns the HRESULT.
//
//  throw_hr, throw_win functions
//      Inline function for checking a result and throwing
//      an exception of type hr_message upon failure.
//
//  THR(), TWS() and variants
//      Macros to supply __FILE__ and __LINE__ at to
//      throw_hr/throw_win so that the file/line is
//      recorded from the source including this header and
//      not this header itself.
//
// Example:
//      try
//      {
//          THR(some_interface_ptr->SomeMethod());
//          HFONT font = TWS(::CreateFontIndirect(&lf));
//          // other stuff that may throw rt::hr_message
//      }
//      catch (const rt::hr_message &bang)
//      {
//          return rt::display_error(bang);
//      }
//
// Alternatively, if RT_NO_THROW is defined when this file
// is processed, the exception object is constructed to obtain
// error message text, writes this text to the debug stream
// and then ATLASSERT(false) is called. This allows <rt/hr.h>
// to be easily used within the sample SDK framework which
// does not use exceptions for error handling.
//
// Copyright (C) 2000-2001, Rich Thomson, all rights reserved.
//
#include <windows.h>
#include <tchar.h>

#if defined(RT_NO_THROW)
#include <atlbase.h>
#endif

namespace rt
{
    /////////////////////////////////////////////////////////
    // hr_message
    //
    // Class for bundling up an HRESULT and a message and a
    // source code file/line number.
    //
    class hr_message
    {
    public:
        hr_message(const TCHAR *file, unsigned line,
                   HRESULT hr = E_FAIL,
                   const TCHAR *message = NULL);
        ~hr_message() {}

        const TCHAR *file() const    { return m_file; }
        unsigned line() const        { return m_line; }
        HRESULT result() const       { return m_result; }
        const TCHAR *message() const { return m_message; }

    private:
        enum
        {
            MESSAGE_LEN = 1024
        };
        const TCHAR *m_file;
        unsigned m_line;
        HRESULT m_result;
        TCHAR m_message[MESSAGE_LEN];
    };

    /////////////////////////////////////////////////////////
    // throw_hr
    //
    // Function that throws an exception when the given
    // HRESULT failed.
    //
    inline HRESULT
    throw_hr(const TCHAR *file, unsigned line,
             HRESULT hr, const TCHAR *message = NULL)
    {
        if (FAILED(hr))
        {
#if defined(RT_NO_THROW)
            hr_message bang(file, line, hr, message);
            ::OutputDebugString(bang.message());
            ATLASSERT(false);
#else
            throw hr_message(file, line, hr, message);
#endif
        }
        return hr;
    }

    /////////////////////////////////////////////////////////
    // throw_win
    //
    // Function that throws an exception when a Win32 return
    // value indicates failure.
    //
    template<typename T>
    inline T
    throw_win(const TCHAR *file, unsigned line,
              T status, const TCHAR *message = NULL,
              int error = GetLastError())
    {
        if (!status)
        {
            throw_hr(file, line, HRESULT_FROM_WIN32(error),
                     message);
        }
        return status;
    }

    /////////////////////////////////////////////////////////
    // display_error
    //
    // Takes an hr_message and displays the message string in
    // a message box and returns the HRESULT value.
    //
    inline HRESULT
    display_error(const hr_message &bang,
                  const TCHAR *title = NULL)
    {
        ::MessageBox(0, bang.message(), title, 0);
        return bang.result();
    }
};

// macros to fill in __FILE__, __LINE__ and _T() automatically

// THR => throw HRESULT
#define THR(hr_) \
    rt::throw_hr(_T(__FILE__), __LINE__, hr_, _T(#hr_))
#define THRM(hr_, msg_) \
    rt::throw_hr(_T(__FILE__), __LINE__, hr_, msg_)
#define THRMT(hr_, msg_) \
    rt::throw_hr(_T(__FILE__), __LINE__, hr_, _T(msg_))


// Win32 has lots of functions that return zero on failure:
// a NULL pointer or handle, a zero return count, etc.
// Most Win32 functions return the error code via
// GetLastError().  Some Win32 functions return the error
// code as a non-zero status.  So throw_win takes both a
// status code and an error code.
//
// TWS   => throw Win32 function status
// TWSM  => Win32 status with message
// TWSMT => Win32 status with message constant needing _T()
#define TWS(status_) \
    rt::throw_win(_T(__FILE__), __LINE__, status_, _T(#status_))
#define TWSM(status_, msg_) \
    rt::throw_win(_T(__FILE__), __LINE__, status_, msg_)
#define TWSMT(status_, msg_) \
    rt::throw_win(_T(__FILE__), __LINE__, status_, _T(msg_))

// variations with error code supplied
#define TWSE(status_, error_) \
rt::throw_win(_T(__FILE__), __LINE__, status_, NULL, error_)
#define TWSME(status_, error_, msg_) \
rt::throw_win(_T(__FILE__), __LINE__, status_, msg_, error_)
#define TWSMTE(status_, err_, msg_) \
rt::throw_win(_T(__FILE__), __LINE__, status_, _T(msg_), err_)

#endif
